/** 
* @mainpage FFT Analysis - Pre-Processing 
* 
* PreProcessing processes audio data into a format that another component 
* can then learn sounds and further samples can be tested against those learned 
* samples to prove it can tell the differences between the samples. Samples are 
* input by a GUI Component. 
* 
* @author Quintin, Grant, Josh 
*/ 

/**@license
* This file is part of PreProcessing.
*
* PreProcessing is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PreProcessing is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PreProcessing.  If not, see <http://www.gnu.org/licenses/>.
* 
*/

/** 
* @file PreProcessing.java 
* 
* @brief This class is part of a component that forms a greater system that 
* analyzes aircraft audio signals. 
* 
* @description Not all audio samples will be in the required format for interaction with 
* flanagan.jar ref: http://www.ee.ucl.ac.uk/~mflanaga/java/index.html, 
* therefore some analysis of the input file/s will be necessary in
* order to determine if conversion of the input file will need to take place. 
* Once the audio file is in the desired format, it will go through flanagan's 
* FFT process and a vector of the output will be delivered to the Learning 
* Component.
**/

/** 
* @class PreProcessing 
* 
* @brief To analyse, convert and output the FFT of a WAV file.  
* 
* @description An audio file will be anaylsed and converted if necessary to undergo 
* Flanagan's FFT process. The output will be passed to learning component in
* the form of a vector of 2 complex numbers ie, (-0.0123154, 0.31235465)
*/ 


package FFTAnalysis;

import flanagan.io.*;
import flanagan.math.*;

import java.io.*;
import java.io.File;
import java.io.InputStream;

// REQUIREMENTS FOR AUDIO FILE FORMATING
import javax.sound.sampled.*;//AudioFileFormat;
import javax.sound.sampled.AudioFormat.*;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.spi.FormatConversionProvider;
/*import javax.sound.sampled.DataLine.*; // USED FOR 'Clip'
import javax.sound.sampled.UnsupportedAudioFileException;*/


// REQUIREMENTS FOR PLOT GRAPH
import flanagan.plot.*;



public class PreProcessing
{
   

/**
 * @brief Set sample rate
 * 
 * @description The input file will be converted to the desired sample rate, 
 * number of channels and valid bits
 * 
 * @param inputFile the path to the file for conversion
 * @param outputFile the path for the converted file to be output
 * 
 * ref: http://docs.oracle.com/javase/tutorial/sound/converters.html
 */
public void setSampleRate(String inputPath, String outputPath)
{
try
    {
	WavFile readWavFile = WavFile.openWavFile(new File(inputPath));
	readWavFile.display();

	long numFrames = readWavFile.getNumFrames();
	int numChannels = 1;//readWavFile.getNumChannels();
	int validBits = 8;//readWavFile.getValidBits();
	long sampleRate = 8000;

	WavFile writeWavFile = WavFile.newWavFile(new File(outputPath), numChannels, numFrames, validBits, sampleRate);

	final int BUF_SIZE = 5001;

        //int[] buffer = new int[BUF_SIZE * numChannels];
        //long[] buffer = new long[BUF_SIZE * numChannels];
	double[] buffer = new double[BUF_SIZE * 2];

	int framesRead = 0;
	int framesWritten = 0;

	do
	{
		framesRead = readWavFile.readFrames(buffer, BUF_SIZE);
		framesWritten = writeWavFile.writeFrames(buffer, BUF_SIZE);
		System.out.printf("%d %d\n", framesRead, framesWritten);
		}
	while (framesRead != 0);
				
	readWavFile.close();
	writeWavFile.close();
	}
	catch (Exception e)
	{
            System.err.println(e);
            e.printStackTrace();
	}
}

//ref: http://docs.oracle.com/javase/tutorial/sound/converters.html
//  convert file to PCM <= not fully done yet
public void ConvertFileToWAVE(String inputPath, String outputPath) 
{
        AudioFileFormat inFileFormat;
        File inFile;
        File outFile;
        
        try {
            inFile = new File(inputPath);
            outFile = new File(outputPath);     
        } catch (NullPointerException ex) {
            System.out.println("Error: one of the ConvertFileToWAVE" +" parameters is null!");
            return;
        }
        try {
            // query file type
            inFileFormat = AudioSystem.getAudioFileFormat(inFile);
            if (inFileFormat.getType() != AudioFileFormat.Type.WAVE) 
            {
                // inFile is not WAVE, so let's try to convert it.
                AudioInputStream inFileAIS = AudioSystem.getAudioInputStream(inFile);
                inFileAIS.reset(); // rewind
                if (AudioSystem.isFileTypeSupported(AudioFileFormat.Type.WAVE, inFileAIS)) 
                {
                    // inFileAIS can be converted to WAVE. 
                    // so write the AudioInputStream to the
                    // output file.
                    AudioSystem.write(inFileAIS, AudioFileFormat.Type.WAVE, outFile);
                    System.out.println("Successfully made WAVE file, "
                    + outFile.getPath() + ", from "
                    + inFileFormat.getType() + " file, " +
                    inFile.getPath() + ".");
                    inFileAIS.close();                   
                    return; // All done now
                } 
                else
                    System.out.println("Warning: WAVE conversion of " 
                    + inFile.getPath()
                    + " is not currently supported by AudioSystem.");
                
            } else
            System.out.println("Input file " + inFile.getPath() +
                " is WAVE." + " Conversion is unnecessary.");
            
        } catch (UnsupportedAudioFileException e) {
            System.out.println("Error: " + inFile.getPath()
                + " is not a supported audio file type!");
            return;
        } catch (IOException e) {
            System.out.println("Error: failure attempting to read " 
            + inFile.getPath() + "!");
            return;
        }
}



    public static void main(String[] args)
    {
        // GRANTS MACHINE
        File fileIn = new File("D:/UNI/U65 - Year3 - Sem1/CSI3303 -  Component Based Software Engineering/Assignment1/FA-18 flyby1.wav");        
        File fileOut = new File("D:/UNI/U65 - Year3 - Sem1/CSI3303 -  Component Based Software Engineering/Assignment2/ConversionTests/convertTestFA-18 flyby1.wav");
        
        // QUINTINS MACHINE
        //File fileIn = new File("C:/Cessna182_1.wav");        
        //File fileOut = new File("C:/audio_convertion/Cessna182_1.wav");
        
       
        
        // READING SOUND FILES
        // *******************
        // http://www.labbookpages.co.uk/audio/javaWavFiles.html  
            try
            {                
                System.out.println("\n--------------------------------------" +
                             "\nWavFile [start]" + 
                             "\n--------------------------------------");   
                
                //try to convert the file into PCM
                /*AudioFormat monoFormat = new AudioFormat(Encoding.PCM_UNSIGNED, 8000, 8, 1, 2, 8000, false);
                
               
                AudioInputStream inFileAIS = null;
                inFileAIS = AudioSystem.getAudioInputStream(fileIn);
                
                System.out.println(inFileAIS.toString());
                System.exit(0);*/                               
                
                
            // Open the wav file specified as the first argument
                WavFile wavFile = WavFile.openWavFile(fileIn);
                //wavFile.display();                
                
            // PLAY AUDIO CLIP
                //File fileIn2 = new File("D:/UNI/U65 - Year3 - Sem1/CSI3303 -  Component Based Software Engineering/Assignment1/600hz.wav");                
                //AudioInputStream audioIn = AudioSystem.getAudioInputStream(fileIn2);
                //Clip clip = AudioSystem.getClip();
                //clip.open(audioIn);
                //clip.start();
                
                
                //Convert sample rate
                PreProcessing test1 = new PreProcessing();
                //System.out.println("This file's sample rate is: " + wavFile.getSampleRate());
                
                
                //test Sample Rate, number of channels and Valid bits convert if necessary
                if (wavFile.getSampleRate() != 8000 || wavFile.getNumChannels() != 1 || wavFile.getValidBits() != 8)
                {
                    //System.out.println("This file's sample rate is: " + wavFile.getSampleRate() + ". It must be 8000");
                    System.out.println("---------------\nConvert Audio FILE HERE\n:---------------");
                    test1.setSampleRate(fileIn.toString(), fileOut.toString());                    
                    WavFile convertedSampleWavFile = WavFile.openWavFile(fileOut);
                   
                    wavFile = convertedSampleWavFile;
                    
                    // Display information about the wav file
                    System.out.println("This file's info after conversion : \n");
                    wavFile.display();
                }
                               
                                   
                // Get the number of audio channels in the wav file
                int numChannels = wavFile.getNumChannels();
                //int numChannels = 1; // <-- Assuming an audio file will always be MONO
                
                // Create a buffer of 1024 frames
                double[] buffer = new double[1024 * numChannels];
                //int buffer[] = new int[100 * numChannels];
                
                int framesRead = 0;
                double min = Double.MAX_VALUE;
                double max = Double.MIN_VALUE;

                
                // SETUP FFT 
                FourierTransform ft = new FourierTransform();
                
                                
                do
                {
                    // Read frames into buffer
                    framesRead = wavFile.readFrames(buffer, 100);//Grant's machine doesn't work with 1024

                    // Loop through frames and look for minimum and maximum value
                    for (int s=0 ; s<framesRead * numChannels ; s++)
                    {
                        if (buffer[s] > max) {max = buffer[s];}
                        if (buffer[s] < min) {min = buffer[s];}                        
                        //System.out.println("Min: " + min + ", Max: " + max);
                        
                        ft.setData(buffer);
                    }                    
                }
                while (framesRead != 0);      
                
                //ft.shortTime(framesRead);
                
                ft.transform();   
                double[] tData = ft.getTransformedDataAsAlternate();
                
                for (int i=0; i<ft.getUsedDataLength(); i++)
                {
                    System.out.println(tData[i]);
                }
                
            /*
            * This method sets the value of the sampling period. 
            * If this is set, it is used by the graphing and printing 
            * methods to convert the cycles per unit grid point to a true frequency.
            */
                    //ft.setDeltaT(1024);
                    
            /*
             * The number of the segments used in powerSpectrum is set by this 
             * method. If the length of the segment and hence the number of 
             * segments have already been set by calling setSegmentLength that 
             * calculated number will be overwritten by this later call to 
             * setSegmentNumber. If neither setSegmentNumber nor setSegmentLength 
             * are called the default value of the segment number is one and the 
             * default value of the segment length is the full data length.
             */
                    ft.setSegmentNumber(2);

                    
                    ft.setRectangular();

                    
                    ft.plotPowerSpectrum("TEST");
                    //ft.plotPowerLog(-1.0D, "TEST");      
                    
                    //Time Frequency graph
                    //ft.plotShortTime("Time Frequency");
                    //ft.plotPowerLog();
                    
                    
                    //int freqN = ft.getShortTimeNumberOfFrequencies();
                    //System.out.println("freqN = " + freqN);
                wavFile.display();
                    
                // Close the wavFile
                wavFile.close();

                // Output the minimum and maximum value
                System.out.printf("Min: %f, Max: %f\n", min, max);    
                
            System.out.println("\n--------------------------------------" +
                             "\nWavFile [end]" + 
                             "\n--------------------------------------");            }
            catch (Exception e)
            {
                System.err.println(e);
            }
            
        

        
        // REF: http://stackoverflow.com/questions/6340999/create-an-arraylist-of-bytes
        /*try
        {
            System.out.println("\n\n--------------------------------------" +
                             "\nByteArrayOutputStream [start]" + 
                             "\n--------------------------------------");
            
            // INPUT fileIn AS A STREAM
            AudioInputStream ais = AudioSystem.getAudioInputStream(fileIn);
            
            // PREPARE BUFFER in TO MEMORY
            BufferedInputStream in = new BufferedInputStream(ais, 1024*32);
            
            // PREPARE BUTTER out VIA MEMORY
            ByteArrayOutputStream out = new ByteArrayOutputStream(); 
            
            // ASSIGN byte array 'dataBuffer' TO OUR DEFENITION
            byte[] dataBuffer = new byte[1024 * 16]; 
            int size = 0; 
            
            // WHILE in STREAMING HAS NOT FINISHED
            // -ASSIGN out bits TO ARRAY
            while ((size = in.read(dataBuffer)) != -1) 
            { 
                out.write(dataBuffer, 0, size); 
            }
            
            // COLLECT ALL bits to byte array
            byte[] bytes = out.toByteArray();    
            
            // PRINT EACH BYTE
            for (int i=0; i<bytes.length; i++)
            {
                System.out.println("[" + i + "] " + bytes[i]);
            }
            
            System.out.println("\n--------------------------------------" +
                             "\nByteArrayOutputStream [end]" + 
                             "\n--------------------------------------");        }
        catch(Exception e)
        {
            System.out.println(e);
        }*/
            
            
        
        // READING SOUND FILES
        // *******************
        // http://docs.oracle.com/javase/tutorial/sound/converters.html
        // http://stackoverflow.com/questions/8283039/how-do-you-use-a-wav-file-in-eclipse
        int totalFramesRead = 0;
        
        try
        {
            AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(fileIn);
            int bytesPerFrame = audioInputStream.getFormat().getFrameSize();
            
            if (bytesPerFrame == AudioSystem.NOT_SPECIFIED) 
            {
                // some audio formats may have unspecified frame size
                // in that case we may read any amount of bytes
                bytesPerFrame = 1;
            }
            
            // Set an arbitrary buffer size of 1024 frames.
            int numBytes = 1024 * bytesPerFrame; 
            byte[] audioBytes = new byte[numBytes];
            try 
            {
                int numBytesRead = 0;
                int numFramesRead = 0;

                // SETUP INPUT TEXT FILE
                BufferedWriter f = new BufferedWriter(new FileWriter("C://test.txt"));
                
            // SET UP FILE HEADER
                // Graph Title
                /*f.write("Test data"); f.newLine();
                
                // X Axis Legend
                f.write("X values"); f.newLine();
                    // X Axis Unit of Measurement
                    f.write("Frames"); f.newLine();
                    
                // Y Axis Legend
                f.write("Y Values"); f.newLine();
                    // Y Axis Unit of Measurement
                    f.write("Sample Size"); f.newLine();
                
                // NUMBER OF CURVES
                f.write("1"); f.newLine();                
                
                // NUMBER POINTS ON CRUVE WITH MAXIUMUM NUMBER OF POINTS
                f.write("5"); f.newLine();                
                
                // NUMBER OF POINTS IN [THIS] CURVE
                f.write("159"); f.newLine();*/          
              
                
                // PLOT GRAPH
                //PlotGraph pg = new PlotGraph(double[] xData, double[] yData);
                
                // Try to read numBytes bytes from the file.
                while ((numBytesRead = audioInputStream.read(audioBytes)) != -1) 
                {
                    // Calculate the number of frames actually read.
                    numFramesRead = numBytesRead / bytesPerFrame;
                    totalFramesRead += numFramesRead;

                    
                    // PRINT TO FILE - 2 COLUMNS [Frame : Byte]
                    // f.write(totalFramesRead + " " + audioBytes[0]);
                       
                    
                    // PRINT TO FILE - 1 COLUMN [Byte]
                    f.write(audioBytes[0] + ".00");
                    f.newLine(); // ADD A NEW LINE
                    f.flush(); // PROCESS
                }
                f.close();
                
                
                //FourierTransform ft = new FourierTransform();
                //ft.setdata(audioBytes);
                
            }
            catch (Exception ex) 
            { 
                System.out.println(ex);
            }
        }
        catch (Exception e) 
        {
            System.out.println(e);
        }
    }
}